
from gae_utils import *
from cache import memcache
from models import User, ProductList

# get all the common imports
from gae_utils import *
import logging
from datetime import datetime, timedelta
from pickle import dumps


TIMEZONE = -4
def localtime(d):
    return d + timedelta(0, 3600*TIMEZONE)
ENV.globals['localtime'] = localtime

def utctime(d):
    return d - timedelta(0, 3600*TIMEZONE)


class valid_user(object):
    def __init__(self, redirect_other='', admin=False, redirect_user=''):
        self.redirect = redirect_other
        self.admin = admin
        self.redirect_user = redirect_user
    def __call__(self, fn):
        def wrapped(*v, **k):
            handler = v[0]
            
            user = User.get_user()
            if user and self.redirect_user and (not self.admin or (self.admin and user.admin)):
                return handler.redirect(self.redirect_user)
            elif self.redirect and not user or (user and self.admin and not user.admin):
                return handler.redirect(self.redirect)
            
            if user:
                user.last_activity = datetime.utcnow()
                user.put()
            handler.user = user
            # run actual method now
            val = fn(*v, **k)
            return val
        return wrapped

""" # not standard enough, some have #'s & some have 'bird' in them..
def plural(val, lbl, optional=False):
    ret = ''
    plural = ''
    lbl = lbl.strip('. ')
    if lbl in ['lb','bag','pc','doz','g','gram','per pc','oz','hd','litre','gallon','bushel','qt','ml']:
        pass
    elif lbl in ['bunch'] or lbl.endswith('sleeve'):
        plural = 'es'
    elif lbl in ['bag']
        pass
    return ret
ENV.filters['plural'] = plural
"""

class MainHandler(RenderHandler):
    @valid_user()
    def get(self):
        if not self.user: 
            return self.login_get()
        elif self.user.admin: 
            return self.admin_get()
        else:
            return self.order_get()
    
    def login_get(self, error=''):
        args = utils.storage(error=error)
        args.current_list = ProductList.get_current()
        self.render('login', args)
    
    def order_get(self):
        args = utils.storage(user=self.user)
        args.current_list = ProductList.get_current()
        if args.current_list:
            args.buyer_list = ProductList.get_buyer_list(user=self.user,
                                                         current_list=args.current_list)
        else:
            args.buyer_list = None
        
        self.render('index', args)
        
    def admin_get(self, notes_updated=False):
        # integrate headers into the data..
        # it would be nice to highlight over-order but units are mixed sometimes so tricky to do
        
        current_list = ProductList.get_current()
        if current_list:
            current_orders = ProductList.all().filter('source =', current_list.key()).fetch(1000)
            current_orders.sort(key=lambda o: o.user.username)
        else:
            current_orders = []
        
        active_order = self.request.get('view') or 'all'
        last_update = ''
        all_orders = None
        if active_order != 'all':
            for order in current_orders:
                if str(order.key()) == active_order:
                    active_order = order
                    break
        else:
            active_order = None
            if current_orders:
                last_dt_order = sorted(current_orders, key=lambda o: o.updated, reverse=True)[0]
                last_update = as_time_ago(last_dt_order.updated) + ' by <a href="/?view=%s">' % last_dt_order.key() + last_dt_order.user.username + '</a>'
            
            # move this to model method and call from the template?
            if current_list: 
                data = current_list.get_data()
            else:
                data = []
            for order in current_orders:
                i = 0
                for row in order.get_data():
                    amount = max(0.0, float(row['ORDER'] or 0.0))
                    data[i]['ORDER'] = float(data[i]['ORDER'] or 0.0) + amount
                    if amount and int(amount) == amount: amount = int(amount)
                    if amount:
                        data[i].setdefault('ORDER-details', []).append('%s %s' % (amount, order.user.username))
                    i += 1
            for row in data:
                if row['ORDER'] and int(row['ORDER']) == row['ORDER']:
                    row['ORDER'] = int(row['ORDER'])
            all_orders = ProductList(name='temp-all-data-list', data=dumps(data))
            
        args = utils.storage(user=self.user, current_list=current_list, current_orders=current_orders,
                             notes_updated=notes_updated, active_order=active_order, last_update=last_update,
                             all_orders=all_orders)
        self.render('admin', args)

    
    @valid_user()
    def post(self):
        if self.user:
            action = self.request.get('action') or ''
            if action == 'order':
                return self.order_post()
            elif action == 'update-notes' and self.user.admin:
                return self.update_notes_post()                
            else:
                logging.error('unknown main handler user post action: %s' % action)
        else:
            return self.login_post()
    
    def login_post(self): # for login
        username = self.request.get('username').lower() or None
        password = User.pwdhash(self.request.get('password') or '')
        
        if not username:
            return self.get("Please enter your username.")
        if not password:
            return self.get("Please enter your password.")
                
        user = User.by_username(username)
        emsg = " Contact us if you've forgotten your username or password."
        if not user:
            return self.login_get("<strong>We couldn't find an account with the username, %s.</strong> %s" % (username, emsg))
            
        if user.password != password:
            return self.login_get('<strong>Incorrect password, please try again.</strong>' + emsg)
        else:
            logging.debug('logging in user: %s' % user.username)
            session = get_session()
            session['uid'] = user.key()
            self.redirect('/')
    
    def update_notes_post(self):
        current_list = ProductList.get_current()
        current_list.notes = self.request.get('notes').strip() or ''
        current_list.put()
        return self.admin_get(notes_updated=True)
    
    def order_post(self):
        args = utils.storage(user=self.user)
        
        args.current_list = ProductList.get_current()
        args.buyer_list = ProductList.get_buyer_list(user=self.user,
                                                     current_list=args.current_list)
        
        source_list_key = self.request.get('source_list_key') or ''
        
        if str(args.current_list.key()) != source_list_key:
            # new source list uploaded while buyer working on their order
            logging.error('product availability changed and lost buyer %s order data' % self.user.username)
            args.error = "Uh-oh! Our product availability has changed.  You'll have to re-enter your order.  Sorry for the hassle."
        else:
            # update buyer_list
            bdata = args.buyer_list.get_data()
            i = 0
            for row in bdata:
                try:
                    # XXX: should fraction be allowed?
                    val = float(self.request.get('amount_%s' % i))
                    if val == int(val): val = int(val)
                except ValueError:
                    val = 0
                if val <= 0: val = 0
                
                row['ORDER'] = val
                i += 1
            args.buyer_list.save_data(bdata)
            args.buyer_list.notes = self.request.get('notes').strip() or ''
            args.buyer_list.put()
            args.saved = True
        
        self.render('index', args)        


class LogoutHandler(RenderHandler):
    @valid_user(redirect_other='/')
    def get(self):
        del self.user.session['uid']
        self.redirect('/')


class ProfileHandler(RenderHandler):
    @valid_user(redirect_other='/')
    def get(self):
        args = utils.storage(user=self.user)
        self.render('profile', args)
    
    @valid_user(redirect_other='/')
    def post(self):
        action = self.request.get('action') or ''
        
        logging.debug('got profile action: %s' % action)
        
        error = ''
        saved = False
        oargs = {}
        if action == 'profile':
            name = self.request.get('name').strip() or ''
            email = self.request.get('email').strip() or ''
            phone = self.request.get('phone').strip() or ''
            address = self.request.get('address').strip() or ''
            
            if not email or not valid_email(email):
                error = 'Invalid email address!'
                oargs = utils.storage(email=email, name=name, phone=phone, address=address)
            else:
                # XXX: could validate phone..
                self.user.email = email
                if name: self.user.name = name
                if phone: self.user.phone = phone
                if address: self.user.address = address
                self.user.put()
                saved = 'Your profile has been updated.'
        elif action == 'profile-password':
            old_pwd = self.request.get('old_password').strip() or ''
            pwd = self.request.get('password').strip() or ''
            pwd2 = self.request.get('password2').strip() or ''
            
            if self.user.password != User.pwdhash(old_pwd):
                error = 'Current password incorrect.'
            elif pwd != pwd2:
                error = 'New passwords do not match.'
            elif len(pwd) < 6:
                error = 'New password is too short, must be at least 6 characters.'
            else:
                self.user.password = User.pwdhash(pwd)
                self.user.put()
                saved = 'Your password has been changed.'
        
        args = utils.storage(user=self.user, error=error, saved=saved)
        args.update(oargs)
        self.render('profile', args)


class AccountListHandler(RenderHandler):
    @valid_user(redirect_other='/', admin=True)
    def get(self):
        users = User.all().order('username').fetch(1000)
        self.render('accounts', dict(user=self.user, users=users))
        
    
class AccountHandler(RenderHandler):
    @valid_user(redirect_other='/', admin=True)
    def get(self):
        edit_key = self.request.get('u') or ''
        auser = utils.storage(email='', username='', name='', phone='', address='', admin=False)
        if edit_key:
            auser = User.get(edit_key)
        self.render('new_edit_account', utils.storage(user=self.user, auser=auser, new_user=not edit_key))
    
    @valid_user(redirect_other='/', admin=True)
    def post(self):
        edit_key = self.request.get('u') or ''
        auser = utils.storage(email='', username='', name='', phone='', address='', admin=False)
        if edit_key:
            auser = User.get(edit_key)
            
        # required
        username = self.request.get('username').strip().lower() or ''
        password = self.request.get('password').strip() or ''
        email = self.request.get('email').strip() or ''
        # optional
        name = self.request.get('name').strip() or ''
        phone = self.request.get('phone').strip() or ''
        address = self.request.get('address').strip() or ''
        
        oargs = utils.storage(username=username, password=password, email=email,
                              name=name, phone=phone, address=address)
        
        error = ''
        saved = False
        if not email or not valid_email(email):
            error = 'Invalid email address!'
        elif edit_key and password and len(password) < 6:
            # only updated if not empty
            error = 'Passwords must be at least 6 characters.'
        elif not edit_key and len(password) < 6:
            error = 'Passwords must be at least 6 characters.'
        elif not username:
            error = 'Missing username.'
        elif edit_key:
            # XXX: could validate phone..
            auser.username = username
            if password: auser.password = User.pwdhash(password)
            auser.email = email
            if name: auser.name = name
            if phone: auser.phone = phone
            if address: auser.address = address
            auser.put()
            saved = 'User account updated.'
        else:
            _user = User(username=username, password=User.pwdhash(password), email=email,
                         name=name or None, phone=phone or None, address=address or None,
                         admin=False)
            _user.put()
            saved = 'User account created.'
        
        args = utils.storage(user=self.user, auser=auser, new_user=not edit_key, error=error, saved=saved)
        if error:
            args.update(oargs)
        
        self.render('new_edit_account', args)


class UploadHandler(RenderHandler):
    @valid_user(redirect_other='/', admin=True)
    def get(self):
        args = utils.storage(user=self.user, now_date=datetime.utcnow())
        self.render('upload', args)

    @valid_user(redirect_other='/', admin=True)
    def post(self):
        args = utils.storage(user=self.user, now_date=datetime.utcnow())
        
        args.data = self.request.get('data') or ''
        #args.name = self.request.get('name') or ''
        args.notes = self.request.get('notes').strip() or ''
        args.date = self.request.get('date').strip() or ''
        
        error = ''
        
        dnums = args.date.split('-')
        derr = False
        if len(dnums) == 3:
            try:
                y,m,d = [int(s) for s in dnums]
                date = utctime(datetime(y,m,d))
            except ValueError:
                derr = True
        else:
            derr = True
        if derr:
            error = 'Invalid date! %s' % args.date
            args.date = args.now_date.strftime('%Y-%m-%d')
            date = args.now_date

        
        if not error and not args.data:
            error = 'Missing Product List data!'  
        #elif not args.name:
        #    error = 'Missing name!'  
        args.error = error
        
        action = self.request.get('action') or 'preview'
        if error or action == 'preview':
            args.product_list = None        
            if not args.error:
                args.product_list = ProductList.create(user=self.user, name='temporary-list', notes=args.notes,
                                                       data=args.data, date=date, save=False)
                logging.debug('args.product_list set, name is %s' % args.product_list.name)
            self.render("upload", args)
        else:
            # no reason to ask for name from user yet since we never show it
            #name = localtime(datetime.utcnow()).strftime('%Y-%m%-d') + ' Product List'
            name = args.date + ' Product List'
            ProductList.create(name=name, user=self.user, notes=args.notes, data=args.data, date=date)
            self.redirect('/')


class EmptyHandler(RenderHandler):
    def get(self):
        # send content-type ..?
        self.write('')


def main():
    logging.getLogger().setLevel(logging.DEBUG)
    
    user = User.all().filter('username =', 'admin').fetch(1)
    if not user:
        # create some test users
        user = User(username='admin',password=User.pwdhash('kegger'),
                    admin=True, email='admin@example.com')
        user.put()
        
        user = User(username='buyer',password=User.pwdhash('kegger'),
                    admin=False,email='client@example.com')
        user.put()

    
    application = webapp.WSGIApplication([('/', MainHandler),
                                          ('/upload', UploadHandler),
                                          ('/logout', LogoutHandler),
                                          ('/profile', ProfileHandler),
                                          ('/accounts/', AccountListHandler),
                                          ('/accounts/new', AccountHandler),
                                          ('/accounts/edit', AccountHandler),
                                          ('/robots.txt', EmptyHandler),
                                          ],
                                         debug=DEV)
  
    wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
    main()
